`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

/**
	@file MemoryMappedDramController.v
	@author Andrew D. Zonenberg
	@brief Memory mapping wrapper around the MCB
	
	Do not send any signals to the controller until calib_done is asserted!
 */
module MemoryMappedDramController(
	clk,
	addr, wr, rd, din, wmask, dout, doutok,
	
	clk_20mhz, clk_ddr_p, clk_ddr_n, clk_mcb_calib, clk_pll_reset, clk_pll_locked,
	
	mcb3_dram_dq, mcb3_dram_a, mcb3_dram_ba, mcb3_dram_cke, mcb3_dram_ras_n, mcb3_dram_cas_n, mcb3_dram_we_n,
	mcb3_dram_dm, mcb3_rzq, mcb3_dram_dqs, mcb3_dram_ck, mcb3_dram_ck_n,
	
	calib_done, error, fifo_full
    );
	 
	////////////////////////////////////////////////////////////////////////////////////////////////
	// IO / parameter declarations
	input wire clk;
	
	//CPU bus
	input wire[31:0] addr;
	input wire wr;
	input wire rd;
	input wire[31:0] din;
	input wire[3:0] wmask;
	output reg[31:0] dout = 0;
	output reg doutok = 0;
	
	input wire clk_20mhz;
	input wire clk_ddr_p;
	input wire clk_ddr_n;
	input wire clk_mcb_calib;
	input wire clk_pll_reset;
	input wire clk_pll_locked;
	
	//DRAM bus
	inout wire[7:0] mcb3_dram_dq;
	output wire[12:0] mcb3_dram_a;
	output wire[1:0] mcb3_dram_ba;
	output wire mcb3_dram_cke;
	output wire mcb3_dram_ras_n;
	output wire mcb3_dram_cas_n;
	output wire mcb3_dram_we_n;
	output wire mcb3_dram_dm;
	inout wire mcb3_rzq;
	output wire mcb3_dram_dqs;
	output wire mcb3_dram_ck;
	output wire mcb3_dram_ck_n;
	
	//Support signals
	output wire calib_done;
	output reg error = 0;
	
	//set "TRUE" for simulation to speed things up
	parameter C3_SIMULATION = "FALSE";
	
	output wire fifo_full;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// The MCB itself
	reg mcb_reset = 0;
	
	//controlled by second fifo
	wire p0_cmd_en;
	wire[2:0] p0_cmd_instr;
	wire[5:0] p0_cmd_bl;
	wire[29:0] p0_cmd_byte_addr;
	wire p0_cmd_empty;
	wire p0_cmd_full;
	
	reg p0_wr_en = 0;
	reg[3:0] p0_wr_mask = 4'hf;	//LOW means write the bit - flipped from mask used on internal bus
	reg[31:0] p0_wr_data = 0;
	wire p0_wr_full;
	wire p0_wr_empty;
	wire[6:0] p0_wr_count;
	wire p0_wr_underrun;
	wire p0_wr_error;
	
	reg p0_rd_en = 0;
	wire[31:0] p0_rd_data;
	wire p0_rd_full;
	wire p0_rd_empty;
	wire[6:0] p0_rd_count;
	wire p0_rd_overflow;
	wire p0_rd_error;
	
	DramController #(.C3_SIMULATION(C3_SIMULATION)) mcb (
		
		.clk_20mhz(clk_20mhz),
		.clk_ddr_p(clk_ddr_p),
		.clk_ddr_n(clk_ddr_n),
		.clk_mcb_calib(clk_mcb_calib),
		.clk_pll_locked(clk_pll_locked),
		
		.mcb3_dram_dq(mcb3_dram_dq), 
		.mcb3_dram_a(mcb3_dram_a), 
		.mcb3_dram_ba(mcb3_dram_ba), 
		.mcb3_dram_cke(mcb3_dram_cke), 
		.mcb3_dram_ras_n(mcb3_dram_ras_n), 
		.mcb3_dram_cas_n(mcb3_dram_cas_n), 
		.mcb3_dram_we_n(mcb3_dram_we_n), 
		.mcb3_dram_dm(mcb3_dram_dm), 
		.mcb3_rzq(mcb3_rzq), 
		.c3_sys_rst_i(clk_pll_reset), 
		.c3_calib_done(calib_done), 
		//.c3_rst0(c3_rst0), 
		.mcb3_dram_dqs(mcb3_dram_dqs), 
		.mcb3_dram_ck(mcb3_dram_ck), 
		.mcb3_dram_ck_n(mcb3_dram_ck_n),
		
		//Port 0 - connected to internal stuff
		.c3_p0_cmd_clk(clk), 
		.c3_p0_cmd_en(p0_cmd_en), 
		.c3_p0_cmd_instr(p0_cmd_instr), 
		.c3_p0_cmd_bl(p0_cmd_bl), 
		.c3_p0_cmd_byte_addr(p0_cmd_byte_addr), 
		.c3_p0_cmd_empty(p0_cmd_empty), 
		.c3_p0_cmd_full(p0_cmd_full), 
		.c3_p0_wr_clk(clk), 
		.c3_p0_wr_en(p0_wr_en), 
		.c3_p0_wr_mask(p0_wr_mask), 
		.c3_p0_wr_data(p0_wr_data), 
		.c3_p0_wr_full(p0_wr_full), 
		.c3_p0_wr_empty(p0_wr_empty), 
		.c3_p0_wr_count(p0_wr_count), 
		.c3_p0_wr_underrun(p0_wr_underrun), 
		.c3_p0_wr_error(p0_wr_error), 
		.c3_p0_rd_clk(clk), 
		.c3_p0_rd_en(p0_rd_en), 
		.c3_p0_rd_data(p0_rd_data), 
		.c3_p0_rd_full(p0_rd_full), 
		.c3_p0_rd_empty(p0_rd_empty), 
		.c3_p0_rd_count(p0_rd_count), 
		.c3_p0_rd_overflow(p0_rd_overflow), 
		.c3_p0_rd_error(p0_rd_error)//, 
		
		/*
		.c3_p1_cmd_clk(c3_p1_cmd_clk), 
		.c3_p1_cmd_en(c3_p1_cmd_en), 
		.c3_p1_cmd_instr(c3_p1_cmd_instr), 
		.c3_p1_cmd_bl(c3_p1_cmd_bl), 
		.c3_p1_cmd_byte_addr(c3_p1_cmd_byte_addr), 
		.c3_p1_cmd_empty(c3_p1_cmd_empty), 
		.c3_p1_cmd_full(c3_p1_cmd_full), 
		.c3_p1_wr_clk(c3_p1_wr_clk), 
		.c3_p1_wr_en(c3_p1_wr_en), 
		.c3_p1_wr_mask(c3_p1_wr_mask), 
		.c3_p1_wr_data(c3_p1_wr_data), 
		.c3_p1_wr_full(c3_p1_wr_full), 
		.c3_p1_wr_empty(c3_p1_wr_empty), 
		.c3_p1_wr_count(c3_p1_wr_count), 
		.c3_p1_wr_underrun(c3_p1_wr_underrun), 
		.c3_p1_wr_error(c3_p1_wr_error), 
		.c3_p1_rd_clk(c3_p1_rd_clk), 
		.c3_p1_rd_en(c3_p1_rd_en), 
		.c3_p1_rd_data(c3_p1_rd_data), 
		.c3_p1_rd_full(c3_p1_rd_full), 
		.c3_p1_rd_empty(c3_p1_rd_empty), 
		.c3_p1_rd_count(c3_p1_rd_count), 
		.c3_p1_rd_overflow(c3_p1_rd_overflow), 
		.c3_p1_rd_error(c3_p1_rd_error), 
		*/
		
		/*
		.c3_p2_cmd_clk(c3_p2_cmd_clk), 
		.c3_p2_cmd_en(c3_p2_cmd_en), 
		.c3_p2_cmd_instr(c3_p2_cmd_instr), 
		.c3_p2_cmd_bl(c3_p2_cmd_bl), 
		.c3_p2_cmd_byte_addr(c3_p2_cmd_byte_addr), 
		.c3_p2_cmd_empty(c3_p2_cmd_empty), 
		.c3_p2_cmd_full(c3_p2_cmd_full), 
		.c3_p2_wr_clk(c3_p2_wr_clk), 
		.c3_p2_wr_en(c3_p2_wr_en), 
		.c3_p2_wr_mask(c3_p2_wr_mask), 
		.c3_p2_wr_data(c3_p2_wr_data), 
		.c3_p2_wr_full(c3_p2_wr_full), 
		.c3_p2_wr_empty(c3_p2_wr_empty), 
		.c3_p2_wr_count(c3_p2_wr_count), 
		.c3_p2_wr_underrun(c3_p2_wr_underrun), 
		.c3_p2_wr_error(c3_p2_wr_error), 
		.c3_p2_rd_clk(c3_p2_rd_clk), 
		.c3_p2_rd_en(c3_p2_rd_en), 
		.c3_p2_rd_data(c3_p2_rd_data), 
		.c3_p2_rd_full(c3_p2_rd_full), 
		.c3_p2_rd_empty(c3_p2_rd_empty), 
		.c3_p2_rd_count(c3_p2_rd_count), 
		.c3_p2_rd_overflow(c3_p2_rd_overflow), 
		.c3_p2_rd_error(c3_p2_rd_error), 
		*/
		
		/*
		.c3_p3_cmd_clk(c3_p3_cmd_clk), 
		.c3_p3_cmd_en(c3_p3_cmd_en), 
		.c3_p3_cmd_instr(c3_p3_cmd_instr), 
		.c3_p3_cmd_bl(c3_p3_cmd_bl), 
		.c3_p3_cmd_byte_addr(c3_p3_cmd_byte_addr), 
		.c3_p3_cmd_empty(c3_p3_cmd_empty), 
		.c3_p3_cmd_full(c3_p3_cmd_full), 
		.c3_p3_wr_clk(c3_p3_wr_clk), 
		.c3_p3_wr_en(c3_p3_wr_en), 
		.c3_p3_wr_mask(c3_p3_wr_mask), 
		.c3_p3_wr_data(c3_p3_wr_data), 
		.c3_p3_wr_full(c3_p3_wr_full), 
		.c3_p3_wr_empty(c3_p3_wr_empty), 
		.c3_p3_wr_count(c3_p3_wr_count), 
		.c3_p3_wr_underrun(c3_p3_wr_underrun), 
		.c3_p3_wr_error(c3_p3_wr_error), 
		.c3_p3_rd_clk(c3_p3_rd_clk), 
		.c3_p3_rd_en(c3_p3_rd_en), 
		.c3_p3_rd_data(c3_p3_rd_data), 
		.c3_p3_rd_full(c3_p3_rd_full), 
		.c3_p3_rd_empty(c3_p3_rd_empty), 
		.c3_p3_rd_count(c3_p3_rd_count), 
		.c3_p3_rd_overflow(c3_p3_rd_overflow), 
		.c3_p3_rd_error(c3_p3_rd_error)
		*/
		);
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// MCB data FIFOs are nice and big but the command FIFO is awfully small for designs that don't
	// do long data bursts. Put a bigger one in front of it!
	
	reg u_cmd_en = 0;
	reg[2:0] u_cmd_instr = 0;
	reg[5:0] u_cmd_bl = 0;
	reg[29:0] u_cmd_byte_addr;
	wire u_cmd_empty;
	wire u_cmd_full;
	
	MCBCommandFifoWrapper mcb_fifo_wrapper (
		.clk(clk), 
		.u_cmd_en(u_cmd_en), 
		.u_cmd_instr(u_cmd_instr), 
		.u_cmd_bl(u_cmd_bl), 
		.u_cmd_byte_addr(u_cmd_byte_addr), 
		.u_cmd_empty(u_cmd_empty), 
		.u_cmd_full(u_cmd_full), 
		.mcb_cmd_en(p0_cmd_en), 
		.mcb_cmd_instr(p0_cmd_instr), 
		.mcb_cmd_bl(p0_cmd_bl), 
		.mcb_cmd_byte_addr(p0_cmd_byte_addr), 
		.mcb_cmd_empty(p0_cmd_empty), 
		.mcb_cmd_full(p0_cmd_full)
		);

	////////////////////////////////////////////////////////////////////////////////////////////////
	// Interface logic
	
	/*
		Possible gotchas found by reading UG388...
		
		MCB write mask is inverted from that of our bus
		Burst length is offset by 1 (bl=0 means write one word, not zero).
	 */
	
	//MCB commands from UG388
	localparam INSTR_WRITE						= 3'b000;
	localparam INSTR_READ 						= 3'b001;
	localparam INSTR_WRITE_AUTO_PRECHARGE	= 3'b010;
	localparam INSTR_READ_AUTO_PRECHARGE	= 3'b011;
	
	//Physical address may or may not be the same as the logical address
	//separated here to make translation easier to add if it's ever added
	wire[29:0] physical_address;
	assign physical_address = addr[29:0];
	
	reg read_active = 0;
	reg[29:0] last_read_addr = 0;
	
	reg[31:0] fifo_dout = 0;
	reg doutrdy = 0;
	
	always @(posedge clk) begin
	
		u_cmd_en <= 0;
		u_cmd_instr <= 0;
		u_cmd_bl <= 0;
		u_cmd_byte_addr <= 0;
		
		p0_rd_en <= 0;
		
		p0_wr_en <= 0;
		p0_wr_mask <= 0;
		p0_wr_data <= 0;
		
		doutok <= 0;
		dout <= 0;
		
		////////////////////////////////////////////////////////////////
		// Real controller here
	
		//Write a single word to RAM
		if(wr) begin
		
			u_cmd_en <= 1;
			u_cmd_instr <= INSTR_WRITE;
			u_cmd_bl <= 0;
			u_cmd_byte_addr <= physical_address;
			
			p0_wr_en <= 1;
			p0_wr_mask <= ~wmask;
			p0_wr_data <= din;
			
		end
				
		//Read a single word from RAM
		//Do not allow reads if we're processing a delayed write
		else if(rd) begin
		
			//If a read has finished, check if it was for our address
			if(doutrdy) begin
				
				//Last read is for this address, it's a hit
				if(physical_address == last_read_addr) begin
					doutok <= 1;
					dout <= fifo_dout;
					
					doutrdy <= 0;
					read_active <= 0;
				end
				
				//Read for the wrong address - throw it away
				else begin
					doutrdy <= 0;
					read_active <= 0;
				end
				
			end
			
			//If read is active then just wait
			//otherwise initiate a read
			else if(!read_active) begin		
				u_cmd_en <= 1;
				u_cmd_instr <= INSTR_READ;
				u_cmd_bl <= 0;
				u_cmd_byte_addr <= physical_address;
			
				read_active <= 1;
				last_read_addr <= physical_address;
						
			end
			
		end
		
		//check for overflow
		if(p0_cmd_full && p0_cmd_en)
			error <= 1;
		
		//Pull data and pop the read FIFO
		if(!p0_rd_empty) begin
			p0_rd_en <= 1;
			fifo_dout <= p0_rd_data;
			doutrdy <= 1;
		end
	
	end

endmodule
